/*
 * Copyright (c) 2009-2010 HIT Microelectronic Center
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *
 * Authors: Gou Pengfei
 *
 * Date: Aug. 2010
 *
 */

#include "base/cp_annotate.hh"
#include "cpu/edge/atomic/atomic_dyn_inst.hh"

template <class Impl>
AtomicEdgeDynInst<Impl>::AtomicEdgeDynInst(EDGEStaticInstPtr staticInst,
                                   Addr PC, Addr NPC,
                                   Addr Pred_PC, Addr Pred_NPC,
                                   InstSeqNum seq_num, CPU *cpu, TheISA::BlockStatus blockstatus)
    : BaseAtomicEdgeDynInst<Impl>(staticInst, PC, NPC,
                        Pred_PC, Pred_NPC, seq_num, cpu, blockstatus)
{
    initVars();
}

template <class Impl>
AtomicEdgeDynInst<Impl>::AtomicEdgeDynInst(ExtMachInst inst,
                                   Addr PC, Addr NPC,
                                   Addr Pred_PC, Addr Pred_NPC,
                                   InstSeqNum seq_num, CPU *cpu, TheISA::BlockStatus blockstatus)
    : BaseAtomicEdgeDynInst<Impl>(inst, PC, NPC,
                        Pred_PC, Pred_NPC, seq_num, cpu, blockstatus)
{
    initVars();
}

template <class Impl>
void
AtomicEdgeDynInst<Impl>::initVars()
{
    // Make sure to have the renamed register entries set to the same
    // as the normal register entries.  It will allow the IQ to work
    // without any modifications.
    for (int i = 0; i < this->staticInst->numDestRegs(); i++) {
        this->_destRegIdx[i] = this->staticInst->destRegIdx(i);
    }

    for (int i = 0; i < this->staticInst->numSrcRegs(); i++) {
        this->_srcRegIdx[i] = this->staticInst->srcRegIdx(i);
    }

    for (int i = 0; i < getNumConsumers(); i++) {
        _consumer[i].id = 0;
        _consumer[i].subtype = TheISA::InvalidSubType;
        _consumer[i].type = TheISA::InvalidType;
    }

    for (int i = 0; i < getNumOperands(); i++) {
        _operand[i].intValue = 0;
        operandValid[i] = false;
    }
    
    resultValid = false;
    _result.intResult = 0;

    readyOperands = 0;

    _predicationResult.pred_status = TheISA::NoPred;

    _predicationResult.valid = false;

    // All of the inst output will be defaulted to general
    // unless they receive NULL or EXCEPTION token.
    _dataflowTokenType = TheISA::General;

    testInstPathID = -1;
}

template<class Impl>
bool
AtomicEdgeDynInst<Impl>::setPredStatus(PredStatus status)
{
    assert(this->staticInst->getPredication() == TheISA::PredUponTrue ||
           this->staticInst->getPredication() == TheISA::PredUponFalse);

    if (this->isPredMatched()) {
        assert(_predicationResult.valid);
        DPRINTF(EdgeIQ, "The predication of this inst"
                " has been marked as ready. No need to"
                " mark it again.\n");
        // The predication of this instruction has been marked as
        // ready before, no need to check if it is ready again.
        return false;
    }

    _predicationResult.pred_status = status;
    _predicationResult.valid = true;

    if (status == TheISA::PredTrue &&
                this->staticInst->getPredication()==
                TheISA::PredUponTrue) {
        DPRINTF(AtomicEdgeCPU,"Pred on True valid on this inst.\n");
        this->setPredMatched();

        // This is a matched predication token
        return true;
    }

    if (status == TheISA::PredFalse &&
                this->staticInst->getPredication()==
                TheISA::PredUponFalse) {
        DPRINTF(AtomicEdgeCPU,"Pred on False valid on this inst.\n");
        this->setPredMatched();

        // This is a matched predication token
        return true;
    }

    // This is not a matched predication token
    return false;
}

template<class Impl>
void
AtomicEdgeDynInst<Impl>::markPredReady()
{
    assert(this->staticInst->getPredication() == TheISA::PredUponTrue ||
        this->staticInst->getPredication() == TheISA::PredUponFalse );

    DPRINTF(AtomicEdgeCPU, "Mark pred ready on inst[Bid:%lli][Iid:%lli].\n",
        this->getBlockID(), this->getInstID());
        
    // We may reach this point when the inst 
    // has been marked as ready to issue. That
    // is because an inst may receive multiple
    // predication token among which only one 
    // is matched.
    if (this->readyToIssue() || !this->isPredMatched()) {
        DPRINTF(AtomicEdgeCPU,"This inst is already ready or the predication"
            " token is not matched, so no need to mark it as ready.\n");
        return;
    }
    
    if ( readyOperands == getNumOperands() ) {
        if ( _predicationResult.valid && this->isPredMatched() ) {
            DPRINTF(AtomicEdgeCPU, "Pred ready on inst[Bid:%lli][Iid:%lli].\n",
                                       this->getBlockID(), this->getInstID());
            this->setCanIssue();
            return;
        }
    }
    return;
}

template<class Impl>
void
AtomicEdgeDynInst<Impl>::markOperandReady()
{
    DPRINTF(AtomicEdgeCPU, "readyOperands = %d, this inst has %d operands\n",
        readyOperands, getNumOperands() );
    assert(readyOperands <= getNumOperands());

    if (++readyOperands == getNumOperands()) {

        if (_predicationResult.valid && this->isPredMatched()) {
            this->setCanIssue();
            return;
        }

        if (this->staticInst->getPredication() == TheISA::Disable || \
                    this->staticInst->getPredication() == TheISA::Reserved) {
            this->setCanIssue();
            return;
        }
    }

    return;
}

template <class Impl>
Fault
AtomicEdgeDynInst<Impl>::execute()
{
    // If there's already a fault storing in this inst, it means the producer
    // of this inst has generated some kind of fault, so just keep the
    // fault and don't need to call execute. The fault will be propagate to
    // the consumers in wake dependent process.
    if (this->fault != NoFault) {
        // Don't execute, set the result to 0 directly
        this->setIntResult(0);
    } else {
        this->fault = this->staticInst->execute(this, this->traceData);
    }
    
    return this->fault;
}

template <class Impl>
Fault
AtomicEdgeDynInst<Impl>::initiateAcc()
{
    // If there's already a fault storing in this inst, it means the producer
    // of this inst has generated some kind of fault, so just keep the
    // fault and don't need to call execute. The fault will be propagate to
    // the consumers in wake dependent process.
    if (this->fault != NoFault) {
        // Don't execute, set the result to 0 directly
        this->setIntResult(0);
    } else {
        this->fault = this->staticInst->initiateAcc(this, this->traceData);
    }

    return this->fault;
}

template<class Impl>
Fault
AtomicEdgeDynInst<Impl>::eaComp()
{
    // If there's already a fault storing in this inst, it means the producer
    // of this inst has generated some kind of fault, so just keep the
    // fault and don't need to call execute. The fault will be propagate to
    // the consumers in wake dependent process.
    if (this->fault != NoFault) {
        // Don't execute, set the result to 0 directly
        this->setIntResult(0);
    } else {
        this->fault = this->staticInst->eaComp(this, this->traceData);
    }

    return this->fault;
}

template <class Impl>
Fault
AtomicEdgeDynInst<Impl>::completeAcc(PacketPtr pkt)
{
    // If there's already a fault storing in this inst, it means the producer
    // of this inst has generated some kind of fault, so just keep the
    // fault and don't need to call execute. The fault will be propagate to
    // the consumers in wake dependent process.
    if (this->fault != NoFault) {
        // Don't execute, set the result to 0 directly
        this->setIntResult(0);
    } else {
        this->fault = this->staticInst->completeAcc(pkt, this, this->traceData);
    }

    return this->fault;
}

